# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-02-17 11:20:24
# @Last Modified by:   bao
# @Last Modified time: 2017-05-15 15:53:56

from gen_topo import *
from gen_topo_fully import *
from datetime import datetime


class gen_topo_detour(gen_topo_fully):
    """
    non-blocking circuit switch with forwarding node constraint (xy, yx)
    """

    def __init__(self):
        gen_topo_fully.__init__(self)
        self.pre_forwards = {}  # {port:{(src1,dst1):forwarding_cp, (src2,dst2):forwarding_cp}}

    def _transform(self, wl_graph, tf_graph, degree, base_len):
        """
        Generate new topology based on multi max_weight_matching (directed bipartite graph)
        node (bp_src, bipartite=0, degree=0)
        node (bp_dst, bipartite=0, degree=0)
        edge (src, dst, 'weight')
        """
        starttime = datetime.now()
        remain_edges = []
        temp_graph = tf_graph.copy()
        while temp_graph.edges():
            match = nx.max_weight_matching(temp_graph, maxcardinality=False)
            # print match
            for src in match:
                dst = match[src]
                if src <= base_len:
                    # print src, dst, temp_graph[src][dst]['weight']
                    max_degree = max(wl_graph.node[src]['degree'], wl_graph.node[dst]['degree'])
                    if max_degree < degree:
                        traffic_weight = get_weight(temp_graph[src][dst]['weight'])
                        wl_max = degree - max_degree
                        wl_num = min(traffic_weight, wl_max)
                        wl_graph.add_edge(src, dst, weight=wl_num)
                        wl_graph.node[src]['degree'] += wl_num
                        wl_graph.node[dst]['degree'] += wl_num
                    else:
                        remain_edges.append((src, dst))
                    temp_graph.remove_edge(src, dst)
        endtime = datetime.now()
        timediff = endtime - starttime
        print(timediff)

    # print '_transform time',timediff

    def _wavelength_adjustment(self, new_matches, d_matches, s_matches, row_size, base_len):
        """
        The new_matches are from minimal wavelength adjustment algorithm (Wavecube)
        Update the new_matches, d_matches(dynamic) and s_matches(static)
        cur_forward : {(11, 24): 28, (6, 20): 24, (1, 32): 20, (13, 26): 30}
        """
        starttime = datetime.now()
        for x in new_matches:
            if x not in self.pre_forwards:
                self.pre_forwards[x] = {}
            cur_forward, cp_set_xy, cp_set_yx = self._non_conflicting(new_matches[x], self.pre_forwards[x], row_size,
                                                                      base_len)

            # update d_matches, s_matches and pre_forwards
            for k in cur_forward:
                if k not in self.pre_forwards[x] or self.pre_forwards[x][k] != cur_forward[k]:

                    # if self.pre_forwards[x][k] != cur_forward[k]:
                    #	print "!!!!"

                    if k not in d_matches[x]:
                        # print "$$$"
                        d_matches[x].append(k)
                        s_matches[x].remove(k)

            self.pre_forwards[x] = cur_forward.copy()
        endtime = datetime.now()
        timediff = endtime - starttime
        print ("wavelength_adjustment%s", timediff)

    # print 'wavelength_adjustment time ',timediff

    def _non_conflicting(self, match, pre_forward, row_size, base_len):
        cur_forward = {}
        cp_set_xy = {}
        cp_set_yx = {}
        # print len(match), match
        for (src, dst) in match:
            (src_axis_x, src_axis_y) = get_cd(src, row_size)
            (dst_axis_x, dst_axis_y) = get_cd(dst - base_len, row_size)
            (Is_same_axis, cp_x, cp_y) = get_cp((src_axis_x, src_axis_y), (dst_axis_x, dst_axis_y), row_size, base_len)
            # print (Is_same_axis, cp_x, cp_y)
            if not Is_same_axis:
                if (src, dst) not in pre_forward:
                    pre_forward[(src, dst)] = 0

                if cp_x not in cp_set_xy:
                    if pre_forward[(src, dst)] == 0 or pre_forward[(src, dst)] == cp_x:
                        cp_set_xy[cp_x] = (src, dst)
                        cur_forward[(src, dst)] = cp_x
                    elif cp_y not in cp_set_yx:
                        cp_set_yx[cp_y] = (src, dst)
                        cur_forward[(src, dst)] = cp_y
                    else:
                        cp_set_xy[cp_x] = (src, dst)
                        cur_forward[(src, dst)] = cp_x
                elif cp_y not in cp_set_yx:
                    cp_set_yx[cp_y] = (src, dst)
                    cur_forward[(src, dst)] = cp_y
                else:
                    counter = []

                    self._adjust(cp_set_xy, cp_set_yx, cur_forward, src, dst, cp_x, 'xy', base_len, row_size, counter)

                    if len(counter) > 100:
                        counter = []
                        self._adjust(cp_set_xy, cp_set_yx, cur_forward, src, dst, cp_y, 'yx', base_len, row_size,
                                     counter)
                    if len(counter) > 100:
                        print("error")
                        # print "conflict"
        '''
		print cur_forward
		print 'cp_set_xy', cp_set_xy
		print 'cp_set_yx', cp_set_yx
		'''
        return cur_forward, cp_set_xy, cp_set_yx

    def _adjust(self, cp_set_xy, cp_set_yx, cur_forward, src, dst, cp, direction, base_len, row_size, counter):
        """
        Adjust the colors of cp in detour network
        @ cp cp
        @ direction xy or yx
        @ counter : indicates how many times recursion
        """
        counter.append(1)
        # print counter
        # print (src,dst,direction,cp)
        if direction == 'xy':
            edge = cp_set_xy[cp]
        else:
            edge = cp_set_yx[cp]

        (src_axis_x, src_axis_y) = get_cd(edge[0], row_size)
        (dst_axis_x, dst_axis_y) = get_cd(edge[1] - base_len, row_size)
        (Is_same_axis, cp_x, cp_y) = get_cp((src_axis_x, src_axis_y), (dst_axis_x, dst_axis_y), row_size, base_len)

        if len(counter) > 100:  # another direction
            return

        if direction == 'xy':
            if cp_y not in cp_set_yx:
                cp_set_yx[cp_y] = (edge[0], edge[1])
                cp_set_xy[cp] = (src, dst)
                cur_forward[(edge[0], edge[1])] = cp_y
                cur_forward[(src, dst)] = cp
                return
            else:
                self._adjust(cp_set_xy, cp_set_yx, cur_forward, edge[0], edge[1], cp_y, 'yx', base_len, row_size,
                             counter)
                cp_set_yx[cp_y] = (edge[0], edge[1])
                cp_set_xy[cp] = (src, dst)
                cur_forward[(edge[0], edge[1])] = cp_y
                cur_forward[(src, dst)] = cp
        else:
            if cp_x not in cp_set_xy:
                cp_set_xy[cp_x] = (edge[0], edge[1])
                cp_set_yx[cp] = (src, dst)
                cur_forward[(edge[0], edge[1])] = cp_x
                cur_forward[(src, dst)] = cp
                return
            else:
                self._adjust(cp_set_xy, cp_set_yx, cur_forward, edge[0], edge[1], cp_x, 'xy', base_len, row_size,
                             counter)
                cp_set_xy[cp_x] = (edge[0], edge[1])
                cp_set_yx[cp] = (src, dst)
                cur_forward[(edge[0], edge[1])] = cp_x
                cur_forward[(src, dst)] = cp
